#include "messageshowarea.h"
#include <QScrollBar>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QFontMetrics>
#include <QPainter>
#include <QPainterPath>
#include "mainwidget.h"
#include <QTimer>
#include "soundrecorder.h"
#include <QMenu>

using namespace model;

/**********************************************************************************************
 * 右边消息展示区域
 **********************************************************************************************/
MessageShowArea::MessageShowArea(QWidget *parent)
    :QScrollArea(parent)
{
    //1.设置了这个属性，才能开启滚动效果
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    this->setWidgetResizable(true);
    //2.设置滚动条的样式
    this->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(46, 46, 46); }");
    this->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0;}");
    this->setStyleSheet("QScrollArea { border: none; }");

    container = new QWidget();
    this->setWidget(container); //告诉滚动区，要监视当前的Widget

    //3.给Widget添加核心布局
    QVBoxLayout* vlayout = new QVBoxLayout();
    vlayout->setSpacing(0);
    vlayout->setContentsMargins(0,0,0,0);
    container->setLayout(vlayout);

    //构造数据，测试消息展示区域
#ifdef TEST_UI
    model::UserInfo userInfo;
    userInfo.userId = QString::number(1000);
    userInfo.nickName = "张三";
    userInfo.description = "记得按时吃饭";
    userInfo.avatar = QIcon(":/resource/lion.png");
    userInfo.phoneNumber = "184123456";
    model::MessageInfo message = model::MessageInfo::makeMessage(model::TEXT_TYPE,"",userInfo,QString("这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息这是一条消息").toUtf8(),"");
    this->addBackMessage(true,message);
    model::MessageInfo message2 = model::MessageInfo::makeMessage(model::TEXT_TYPE,"",userInfo,QString("这是一条消息").toUtf8(),"");
    this->addBackMessage(true,message2);
    for(int i = 0; i < 10; ++i)
    {
        model::UserInfo userInfo;
        userInfo.userId = QString::number(1000 + i);
        userInfo.nickName = "张三";
        userInfo.description = "记得按时吃饭";
        userInfo.avatar = QIcon(":/resource/lion.png");
        userInfo.phoneNumber = "184123456";
        model::MessageInfo message = model::MessageInfo::makeMessage(model::TEXT_TYPE,"",userInfo,QString("这是一条消息").toUtf8(),"");
        this->addBackMessage(false,message);
    }
#endif
}

void MessageShowArea::addFrontMessage(bool isLeft, const MessageInfo &message)
{
    //构造MessageItem,添加到布局管理器中
    MessageItem* messageItem = MessageItem::makeMessageItem(isLeft,message);
    QVBoxLayout* vlayout = dynamic_cast<QVBoxLayout*>(container->layout());
    vlayout->insertWidget(0,messageItem);
}

void MessageShowArea::addBackMessage(bool isLeft, const MessageInfo &message)
{
    MessageItem* messageItem = MessageItem::makeMessageItem(isLeft,message);
    container->layout()->addWidget(messageItem);
}

void MessageShowArea::clear()
{
    // 遍历布局管理器, 删除里面的元素
    QLayout* layout = container->layout();
    for (int i = layout->count() - 1; i >= 0; --i) {
        QLayoutItem* item = layout->takeAt(i);
        if (item != nullptr && item->widget() != nullptr) {
            delete item->widget();
        }
    }
}

void MessageShowArea::scrollToEnd()
{
    QTimer* timer = new QTimer();
    connect(timer,&QTimer::timeout,this,[=](){
        //获取到滚动条的最大值
        int maxValue = this->verticalScrollBar()->maximum();
        //设置滚动条到最大位置
        this->verticalScrollBar()->setValue(maxValue);

        timer->stop();
        timer->deleteLater();
    });
    timer->start(500);
}



/**********************************************************************************************
 * 一条包含头像、时间、内容的消息
 * 其中，内容可以是文本、文件、语音、图片
 **********************************************************************************************/
MessageItem::MessageItem(bool isLeft)
    :isLeft(isLeft)
{

}

MessageItem *MessageItem::makeMessageItem(bool isLeft, const MessageInfo &message)
{
    MessageItem* messageItem = new MessageItem(isLeft);
    //1.创建基本骨架，使用网格布局管理消息体内部
    QGridLayout* glayout = new QGridLayout();
    glayout->setSpacing(0);
    glayout->setContentsMargins(10,15,10,0);

    messageItem->setMinimumHeight(100);
    messageItem->setLayout(glayout);

    //2.创建头像
    QPushButton* avatar = new QPushButton();
    avatar->setFixedSize(40,40);
    avatar->setIconSize(QSize(40,40));
    avatar->setIcon(message.userInfo.avatar);
    avatar->setStyleSheet("QPushButton {border : nonde;}");
    if(isLeft)
    {
        //若是左侧消息
        glayout->addWidget(avatar,0,0,2,1,Qt::AlignTop | Qt::AlignLeft);
    }
    else
    {
        glayout->addWidget(avatar,0,1,2,1,Qt::AlignTop | Qt::AlignLeft);
    }

    //3.创建名称和时间
    QLabel* nameLabel = new QLabel();
    nameLabel->setText(message.userInfo.nickName + " | " + message.time);
    nameLabel->setStyleSheet("QLabel{font-size:12px; color: rgb(178,178,178);}");
    if(isLeft)
    {
        glayout->addWidget(nameLabel,0,1);
    }
    else
    {
        glayout->addWidget(nameLabel,0,0,Qt::AlignRight);
    }

    //4.根据类型，创建不同的消息
    QWidget* contentWidget = nullptr;
    switch(message.messageType)
    {
    case model::TEXT_TYPE:
        contentWidget = makeTextContent(isLeft,message.content);
        break;
    case model::FILE_TYPE:
        contentWidget = makeFileContent(isLeft,message);
        break;
    case model::IMAGE_TYPE:
        contentWidget = makeImageContent(isLeft,message.fileId,message.content);
        break;
    case model::SPEECH_TYPE:
        contentWidget = makeSpeechContent(isLeft,message);
        break;
    default:
        LOG() << "消息类型错误，messageType = " << message.messageType;
        break;
    }

    if(isLeft)
    {
        glayout->addWidget(contentWidget,1,1);
    }
    else
    {
        glayout->addWidget(contentWidget,1,0);
    }

    //连接信号槽，处理点击好友头像，显示好友信息
    connect(avatar,&QPushButton::clicked,messageItem,[=](){
        MainWidget* mainWidget = MainWidget::getInstance(); //设置当前Dialog的父窗口时MainWidget
        UserInfoWidget* userInfoWidget = new UserInfoWidget(mainWidget,message.userInfo);
        userInfoWidget->exec();
    });

    //修改昵称后，消息展示区中的消息名称也需要修改
    if(!isLeft)
    {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter,&DataCenter::changeNickNameDone,messageItem,[=](){
            nameLabel->setText(dataCenter->getMyself()->nickName + " | " + message.time);
        });

        // 调整头像
        connect(dataCenter, &DataCenter::changeAvatarDone, messageItem, [=]() {
            avatar->setIcon(dataCenter->getMyself()->avatar);
        });
    }

    return messageItem;
}

QWidget *MessageItem::makeTextContent(bool isLeft,const QString& message)
{
    MessageContentLabel* contentLabel = new MessageContentLabel(isLeft,message,"",model::TEXT_TYPE,QByteArray());
    return contentLabel;
}

QWidget *MessageItem::makeFileContent(bool isLeft, const MessageInfo &message)
{
    MessageContentLabel* contentLabel = new MessageContentLabel(isLeft,"[文件] " + message.fileName,message.fileId,
                                                                message.messageType,message.content);
    return contentLabel;
}

QWidget *MessageItem::makeImageContent(bool isLeft,const QString& fileId,const QByteArray& iamgeContent)
{
    MessageImageLabel* imageLabel = new MessageImageLabel(isLeft,fileId,iamgeContent);
    return imageLabel;
}

QWidget *MessageItem::makeSpeechContent(bool isLeft,const MessageInfo& message)
{
    MessageContentLabel* speechLabel = new MessageContentLabel(isLeft,"[语音]",message.fileId,message.messageType,message.content);
    return speechLabel;
}


/**********************************************************************************************
 * 创建类表示 "文本消息" 正文部分
 **********************************************************************************************/
MessageContentLabel::MessageContentLabel(bool isLeft,const QString &text,const QString& fileId,
                                         const model::MessageType messageType,const QByteArray& fileContent)
    :isLeft(isLeft)
    ,fileId(fileId)
    ,fileContent(fileContent)
    ,messageType(messageType)
{
    // 设置一下 SizePolicy
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    //1.创建消息框中的内容
    QFont font;
    font.setFamily("微软雅黑");
    font.setPixelSize(16);

    textLabel = new QLabel(this);
    textLabel->setText(text);
    textLabel->setFont(font);
    textLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    textLabel->setWordWrap(true);   //自动换行
    this->textLabel->setStyleSheet("QLabel { padding: 0 10px; line-height: 1.2; background-color: transparent; }");
    //2.绘制消息框，重写paintEvent方法

    if(messageType == model::TEXT_TYPE)
    {
        return;
    }

    //文件消息,发送网络请求，进行获取
    if(this->fileContent.isEmpty())
    {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter,&DataCenter::getSingleFileDone,this,&MessageContentLabel::updateUI);
        // LOG() << "文件消息,发送网络请求，进行获取,文件ID" << this->fileId;
        dataCenter->getSingleFileAsync(this->fileId);
    }
    else{
        //content不为空，数据是现成的了，加载状态直接设置为true
        this->loadContentDone = true;
    }
}

void MessageContentLabel::paintEvent(QPaintEvent *event)
{
    (void)event;

    //1.获取其父元素(MessageItem)的宽度
    QObject* object = this->parent();
    if(!object->isWidgetType())
    {
        LOG() << "父元素不是预期的 QWidget, 此时不需要进行任何后续的绘制操作";
        return;
    }

    //2.计算消息气泡宽度
    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width = parent->width() * 0.6;  //一条消息仅占据一个MessageItem宽度的60%;

    //3.统计文本需要占据的宽度
    QFontMetrics metrics(this->textLabel->text());
    int textWidth = metrics.horizontalAdvance(this->textLabel->text());

    //4.计算当前文本需要占据多少行
    //40表示消息气泡左右的边距
    int rows = (textWidth / (width-30)) + 1;
    if(rows == 1)
    {
        //一行能够放下
        width = textWidth + 30;
    }

    //5.根据行数，计算高度
    int height = rows * (this->textLabel->font().pixelSize() * 1.2) + 20;

    //6.绘制消息气泡(圆角矩形 + 三角)
    QPainter painter(this);
    QPainterPath path;
    //设置抗锯齿
    painter.setRenderHint(QPainter::Antialiasing);
    if(isLeft)
    {
        //白色
        painter.setPen(QPen(QColor(255,255,255)));
        painter.setBrush(QColor(255,255,255));

        //绘制圆角矩形
        painter.drawRoundedRect(10,0,width,height,10,10);
        //绘制箭头
        path.moveTo(10,15);
        path.lineTo(0,20);
        path.lineTo(10,25);
        path.closeSubpath();    //形成闭合图形
        painter.drawPath(path); //绘制

        textLabel->setGeometry(10,0,width,height);  //设置消息内容
    }
    else
    {
        //绿色
        painter.setPen(QPen(QColor(137, 217, 97)));
        painter.setBrush(QColor(137, 217, 97));

        //圆角矩形左、右侧横坐标
        int leftPos = this->width() - width - 10;   //10用来放三角形
        int rightPos = this->width() - 10;

        //绘制圆角矩形
        painter.drawRoundedRect(leftPos,0,width,height,10,10);
        //绘制箭头
        path.moveTo(rightPos,15);
        path.lineTo(this->width(),20);
        path.lineTo(rightPos,25);
        path.closeSubpath();
        painter.drawPath(path);

        this->textLabel->setGeometry(leftPos,0,width,height);
    }

    //6.重新设置父元素的高度确保能够容纳上面绘制的消息内容区域
    //注意高度要涵盖之前名字和时间的 label 的高度, 以及留点冗余空间.
    parent->setFixedHeight(height + 50);
}

void MessageContentLabel::mousePressEvent(QMouseEvent *event)
{
    //实现点击文件另存为
    //鼠标左键有效
    if(event->button() == Qt::LeftButton)
    {
        //鼠标左键按下，并且当前消息是文件类型
        if(this->messageType == model::FILE_TYPE)
        {
            //并且文件通过网络加载完毕了
            if(!this->loadContentDone)
            {
                Toast::showMessage("文件数据尚未加载成功，请稍后再试!");
                return;
            }

            //将文件另存
            saveFile(this->fileContent);
        }
        else if(this->messageType == model::SPEECH_TYPE)
        {
            if(!this->loadContentDone)
            {
                Toast::showMessage("语音数据尚未加载成功，请稍后再试!");
                return;
            }

            this->textLabel->setText("播放中...");
            SoundRecorder* soundRecorder = SoundRecorder::getInstance();
            connect(soundRecorder,&SoundRecorder::soundPlayDone,this,&MessageContentLabel::playDone,Qt::UniqueConnection);
            soundRecorder->startPlay(this->fileContent);

        }else
        {
            //其它消息，啥也不做
        }
    }
}

void MessageContentLabel::contextMenuEvent(QContextMenuEvent *event)
{
    (void)event;
    if(messageType != model::SPEECH_TYPE)
    {
        LOG() << "非语音消息不支持右键菜单";
        return;
    }

    QMenu* menu = new QMenu(this);
    QAction* action = menu->addAction("语音转文字");
    connect(action,&QAction::triggered,this,[=](){
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter,&DataCenter::speechConvertTextDone,this,&MessageContentLabel::speechConvertTextDone,Qt::UniqueConnection);
        dataCenter->speechConvertTextAsync(this->fileId,this->fileContent);
    });

    //此处要弹出对应的菜单
    menu->exec(event->globalPos());
    delete menu;    //显示后，销毁
}

void MessageContentLabel::speechConvertTextDone(const QString &fileId, const QString &speechContent)
{
    if(this->fileId != fileId)
    {
        return;
    }

    //修改界面内容
    this->textLabel->setText("[语音转文字]" + speechContent);
    this->update();
}

void MessageContentLabel::updateUI(const QString &fileId, const QByteArray &fileContent)
{
    //所有的MessageContentLabel都会收到该信号
    if(this->fileId != fileId)
    {
        return;
    }

    this->fileContent = fileContent;
    this->loadContentDone = true;
    this->update();
}

void MessageContentLabel::playDone()
{
    if(this->textLabel->text() == "播放中...")
    {
        this->textLabel->setText("[语音]");
    }
}

void MessageContentLabel::saveFile(const QByteArray &fileContent)
{
    QString filePath = QFileDialog::getSaveFileName(this,"另存为",QDir::homePath(),"*");
    if(filePath.isEmpty())
    {
        LOG() << "用户取消另存文件";
        return;
    }
    writeByteArray2File(filePath,fileContent);
}


/**********************************************************************************************
 * 图片消息
 **********************************************************************************************/
MessageImageLabel::MessageImageLabel(bool isLeft,const QString &fileId, const QByteArray &imageContent)
    :fileId(fileId)
    ,imageContent(imageContent)
    ,isLeft(isLeft)
{
    this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    imageBtn = new QPushButton(this);
    imageBtn->setStyleSheet("QPushButton {border : none;}");

    //图片内容为空，说明是仅从服务器拿的fileId，内容还没拿
    if(imageContent.isEmpty())
    {
        //拿着fileId,从服务器获取内容
        DataCenter* dataCenter = DataCenter::getInstance();
        //内容拿到了，更新界面
        connect(dataCenter,&DataCenter::getSingleFileDone,this,&MessageImageLabel::updataUI);
        dataCenter->getSingleFileAsync(fileId);
    }
}

void MessageImageLabel::updataUI(const QString& fileId,const QByteArray& fileContent)
{
    //每个MessageImageLabel都会收到该信号,进入该函数
    if(this->fileId != fileId)
    {
        return; //不是当前从服务器拿下来的，别的时候拿的，不应该再次加载
    }
    this->imageContent = fileContent;

    //触发paintEvent事件，绘制图片消息样式
    this->update();
}

void MessageImageLabel::paintEvent(QPaintEvent *event)
{
    (void)event;
    //1.拿到图片的父元素，查看其宽度是多少，当前图片占据60%
    QObject* object = this->parent();
    if(!object->isWidgetType()){
        return;
    }
    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width = parent->width() * 0.4;
    //2.加载二进制数据作为图片
    QImage image;
    if(imageContent.isEmpty()){
        //此时向服务器请求的图片响应没有回来，为了美观，临时顶替一下
        QByteArray tmpContent = loadFile2ByteArray("/resource/LOGO.png");
        image.loadFromData(tmpContent);
    }
    else{
        image.loadFromData(imageContent);
    }

    //3.针对图片进行缩放
    int height = 0;
    if(image.width() > width){
        //图片宽度过宽，进行等比例缩放
        height = ((double)image.height() / image.width()) * width;
    }
    else{
        //保持不变
        width = image.width();
        height = image.height();
    }
    QPixmap pixmap = QPixmap::fromImage(image);
    imageBtn->setIcon(QIcon(pixmap));
    imageBtn->setIconSize(QSize(width, height));

    //4.由于图片的高度是计算出来的，还需要考虑其父元素能够容纳下 包括图片+姓名+事件
    parent->setFixedHeight(height + 50);
    if(isLeft){
        imageBtn->setGeometry(10,0,width,height);
    }
    else{
        int leftPos = this->width() - width - 5;
        imageBtn->setGeometry(leftPos,0,width,height);
    }
}















